#include "../Headers/USB_Handler.h"


char USB_Out_Buffer[16];
uint8_t totalPacketLength = 0;
uint8_t packetBuffer[20];

void initializeUSB(void) {

    unsigned char i;

#if defined(USB_INTERRUPT)
    USBDeviceAttach();
#endif
#if defined(USE_USB_BUS_SENSE_IO)
    tris_usb_bus_sense = INPUT_PIN; // See HardwareProfile.h
#endif

#if defined(USE_SELF_POWER_SENSE_IO)
    tris_self_power = INPUT_PIN; // See HardwareProfile.h
#endif


    for (i = 0; i<sizeof (USB_Out_Buffer); i++) {
        USB_Out_Buffer[i] = 0;
    }

    USBDeviceInit();
}

void sendIRCodes(void) {
    int i;
    packetBuffer[0] = PACKET_START;
    packetBuffer[1] = 14;
    packetBuffer[2] = STORED_IR_CMD;
    packetBuffer[3] = DataEERead(11);
    packetBuffer[4] = DataEERead(12);
    for (i=0; i<12; i++ )
        packetBuffer[5+i] = DataEERead(i);
    putUSBUSART(&packetBuffer, 20);
}

/* Encodes recieved bytes to form a packet to process*/

void recieveUSBPacket(char *_USB_In_Buffer, uint8_t length) {
   
    int i;
    if (totalPacketLength == 0 && _USB_In_Buffer[0] == PACKET_START) {
        packetBuffer[totalPacketLength] = PACKET_START;
        totalPacketLength++;

    } else if (totalPacketLength > 0) {
        for (i = 0; i < length; i++) {
            packetBuffer[i + totalPacketLength] = _USB_In_Buffer[i];
            totalPacketLength++;
        }
        if (totalPacketLength > 19) {
            totalPacketLength = 0;
            receivedUSBPacketHandler();
        }
    }
}



/* Processes recieved packet */

void receivedUSBPacketHandler(void) {

   int i,offsetEE;

    switch (packetBuffer[2]) {

        case HI_CMD:
            packetBuffer[0] = PACKET_START;
            packetBuffer[1] = HI_CMD;
            putUSBUSART(&packetBuffer, 2);
            break;

        case SET_OUTPUTS_CMD:
            setOutputColors(bytesToWord(packetBuffer[3],packetBuffer[4]),bytesToWord(packetBuffer[5],packetBuffer[6]),bytesToWord(packetBuffer[7],packetBuffer[8]));
           
            break;

        case SET_PRESET_CMD:
            
            for (i=0; i<16; i++)
                DataEEWrite(packetBuffer[i+3],packetBuffer[3]*PRESETS_EE_OFFSET+i);   
            break;

        case SET_IR_CMD:
           setIRKey(packetBuffer[3]);
           break;

        case STORED_IR_CMD:
            sendIRCodes();
            break;


        default: break;

    }
    CDCTxService();
}





#if defined(USB_CDC_SET_LINE_CODING_HANDLER)

void mySetLineCodingHandler(void) {
    //If the request is not in a valid range
    if (cdc_notice.GetLineCoding.dwDTERate.Val > 115200) {
        //NOTE: There are two ways that an unsupported baud rate could be
        //handled.  The first is just to ignore the request and don't change
        //the values.  That is what is currently implemented in this function.
        //The second possible method is to stall the STATUS stage of the request.
        //STALLing the STATUS stage will cause an exception to be thrown in the
        //requesting application.  Some programs, like HyperTerminal, handle the
        //exception properly and give a pop-up box indicating that the request
        //settings are not valid.  Any application that does not handle the
        //exception correctly will likely crash when this requiest fails.  For
        //the sake of example the code required to STALL the status stage of the
        //request is provided below.  It has been left out so that this demo
        //does not cause applications without the required exception handling
        //to crash.
        //---------------------------------------
        //USBStallEndpoint(0,1);
    } else {
        //Update the baudrate info in the CDC driver
        CDCSetBaudRate(cdc_notice.GetLineCoding.dwDTERate.Val);

        //Update the baudrate of the UART

 /*        DWORD_VAL dwBaud;
            dwBaud.Val = (DWORD) (GetSystemClock() / 4) / line_coding.dwDTERate.Val - 1;
            SPBRG = dwBaud.v[0];
            SPBRGH = dwBaud.v[1];
       
*/
    }
}
#endif
 

void USBCBSuspend(void) {

}

void USBCBWakeFromSuspend(void) {
    
}

void USBCBErrorHandler(void) {
    
}

void USBCBCheckOtherReq(void) {
    USBCheckCDCRequest();
}//end

void USBCBStdSetDscHandler(void) {
    
}//end

void USBCBInitEP(void) {
    CDCInitEP();
}

void USBCBSendResume(void) {
    static WORD delay_count;

    //First verify that the host has armed us to perform remote wakeup.
    //It does this by sending a SET_FEATURE request to enable remote wakeup,
    //usually just before the host goes to standby mode (note: it will only
    //send this SET_FEATURE request if the configuration descriptor declares
    //the device as remote wakeup capable, AND, if the feature is enabled
    //on the host (ex: on Windows based hosts, in the device manager
    //properties page for the USB device, power management tab, the
    //"Allow this device to bring the computer out of standby." checkbox
    //should be checked).
    if (USBGetRemoteWakeupStatus() == TRUE) {
        //Verify that the USB bus is in fact suspended, before we send
        //remote wakeup signalling.
        if (USBIsBusSuspended() == TRUE) {
            USBMaskInterrupts();

            //Clock switch to settings consistent with normal USB operation.
            USBCBWakeFromSuspend();
            USBSuspendControl = 0;
            USBBusIsSuspended = FALSE; //So we don't execute this code again,
            //until a new suspend condition is detected.

            //Section 7.1.7.7 of the USB 2.0 specifications indicates a USB
            //device must continuously see 5ms+ of idle on the bus, before it sends
            //remote wakeup signalling.  One way to be certain that this parameter
            //gets met, is to add a 2ms+ blocking delay here (2ms plus at
            //least 3ms from bus idle to USBIsBusSuspended() == TRUE, yeilds
            //5ms+ total delay since start of idle).
            delay_count = 3600U;
            do {
                delay_count--;
            } while (delay_count);

            //Now drive the resume K-state signalling onto the USB bus.
            USBResumeControl = 1; // Start RESUME signaling
            delay_count = 1800U; // Set RESUME line for 1-13 ms
            do {
                delay_count--;
            } while (delay_count);
            USBResumeControl = 0; //Finished driving resume signalling

            USBUnmaskInterrupts();
        }
    }
}



BOOL USER_USB_CALLBACK_EVENT_HANDLER(USB_EVENT event, void *pdata, WORD size) {
    switch ((INT) event) {
        case EVENT_TRANSFER:
            //Add application specific callback task or callback function here if desired.
            break;
        case EVENT_SOF:
            //USBCB_SOF_Handler();
            //TODO: ADD Debug MEssage
            break;
        case EVENT_SUSPEND:
            USBCBSuspend();
            break;
        case EVENT_RESUME:
            USBCBWakeFromSuspend();
            break;
        case EVENT_CONFIGURED:
            USBCBInitEP();
            break;
        case EVENT_SET_DESCRIPTOR:
            USBCBStdSetDscHandler();
            break;
        case EVENT_EP0_REQUEST:
            USBCBCheckOtherReq();
            break;
        case EVENT_BUS_ERROR:
            USBCBErrorHandler();
            break;
        case EVENT_TRANSFER_TERMINATED:
            //Add application specific callback task or callback function here if desired.
            //The EVENT_TRANSFER_TERMINATED event occurs when the host performs a CLEAR
            //FEATURE (endpoint halt) request on an application endpoint which was
            //previously armed (UOWN was = 1).  Here would be a good place to:
            //1.  Determine which endpoint the transaction that just got terminated was
            //      on, by checking the handle value in the *pdata.
            //2.  Re-arm the endpoint if desired (typically would be the case for OUT
            //      endpoints).
            break;
        default:
            break;
    }
    return TRUE;
}